/**
 * Copyright(c) ${copyrightYear} ${company}
 * All rights reserved.
 * Created on ${date}
 */
package ${package}.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;

import ${package}.BaseDTO;
import ${package}.BaseEntity;
import ${package}.dto.annotation.DTOField;

/**
 * DTO拷贝工具类
 * 
 * @author ${author}
 */
public final class DTOUtil {
	/**
	 * 默认构造方法
	 */
	private DTOUtil() {

	}

	/**
	 * 从Entity拷贝数据到dto，根据class自动生成dto，并返回。
	 * 
	 * @param <T>
	 * @param dtoClass
	 * @param entity
	 * @return
	 * @throws Exception
	 */
	public static <T extends BaseDTO> T copyFromEntity(Class<T> dtoClass,
			BaseEntity<?> entity) throws Exception {
		T dto = dtoClass.newInstance();
		copyFromEntity(dto, entity);
		return dto;
	}

	/**
	 * 把DTO拷贝给Entity
	 * 
	 * @param dtoClass
	 * @param entity
	 * @throws Exception
	 */
	public static void copyToEntity(BaseDTO dtoClass, BaseEntity<?> entity)
			throws Exception {
		BeanUtils.copyProperties(dtoClass, entity);
	}

	/**
	 * 从Entity拷贝数据到dto，根据传入的dto进行拷贝。
	 */
	public static void copyFromEntity(BaseDTO dto, BaseEntity<?> entity)
			throws Exception {
		Class<?> dtoClass = dto.getClass();
		Class<?> entityCass = entity.getClass();
		List<Field> dtoFields = getAllField(dtoClass);
		Map<String, Method> entityGetMethods = getAllGetOrSetMethodToMap(
				entityCass, true);
		String fieldCptName = null;
		Class<?> fieldTypeClass = null;
		for (Field field : dtoFields) {
			fieldCptName = StringUtils.capitalize(field.getName());
			fieldTypeClass = field.getType();
			Method setMethod = null;
			try {
				setMethod = dtoClass.getMethod("set" + fieldCptName,
						fieldTypeClass);
			} catch (NoSuchMethodException e) {
				// ingore
				continue;
			}
			// 如果dto有set方法
			if (setMethod != null) {
				DTOField dtoField = field.getAnnotation(DTOField.class);
				if (dtoField != null) {
					// 如果有DTOField注解，就解析注解。
					if (dtoField.ingore()) {
						// 忽略，就不进行赋值操作。
						continue;
					} else {
						if (dtoField.expression() == null
								|| dtoField.expression().trim().isEmpty()) {
							// 不忽略，但是又没有表达式，还是用set get方式进行复制
							get2Set(dto, setMethod, entity, entityGetMethods);
						} else {
							// 不忽略，有表达式，那么就用表达式进行赋值
							String expression = dtoField.expression();
							String[] fieldNames = expression.split("\\.");
							Object result = entity;
							Map<String, Method> methods = entityGetMethods;
							for (String fieldName : fieldNames) {
								Method method = methods.get("get"
										+ StringUtils.capitalize(fieldName));
								if (method != null && result != null) {
									result = method.invoke(result);
									if (result != null) {
										methods = getAllGetOrSetMethodToMap(
												result.getClass(), true);
									}
								}
							}
							setMethod.invoke(dto, result);
						}
					}
				} else {
					// 1:如果没有DTOField注解，直接用get得到的值放入到set中。
					// 2:如果有注解，但是又不忽略，而且表达式为null。
					// 3:满足以上2个条件，都进行默认的set get方法
					get2Set(dto, setMethod, entity, entityGetMethods);
				}
			}
		}
	}

	/**
	 * 获取所有的域
	 * 
	 * @param dtoClass
	 * @return
	 */
	private static List<Field> getAllField(Class<?> dtoClass) {
		List<Field> fields = new ArrayList<Field>();
		while (!dtoClass.equals(Object.class)) {
			fields.addAll(Arrays.asList(dtoClass.getDeclaredFields()));
			dtoClass = dtoClass.getSuperclass();
		}
		return fields;
	}

	/**
	 * 获取所有的get或者set方法
	 * 
	 * @param dtoClass
	 * @return
	 */
	private static Map<String, Method> getAllGetOrSetMethodToMap(
			Class<?> dtoClass, boolean get) {
		String prefix = "get";
		if (!get) {
			prefix = "set";
		}
		Map<String, Method> methods = new HashMap<String, Method>();
		while (!dtoClass.equals(Object.class)) {
			for (Method method : dtoClass.getDeclaredMethods()) {
				if (method.getName().startsWith(prefix)) {
					methods.put(method.getName(), method);
				}
			}
			dtoClass = dtoClass.getSuperclass();
		}
		return methods;
	}

	/**
	 * get到set
	 * 
	 * @param dto
	 * @param entity
	 * @param entityCass
	 * @param fieldCptName
	 * @param fieldDeclareClass
	 * @param setMethod
	 * @throws NoSuchMethodException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	private static void get2Set(BaseDTO dto, Method setMethod,
			BaseEntity entity, Map<String, Method> getMethods) throws Exception {
		String getMethodName = setMethod.getName().replaceFirst("set", "get");
		if (getMethods.containsKey(getMethodName)) {
			Object obj = getMethods.get(getMethodName).invoke(entity);
			setMethod.invoke(dto, obj);
		}
	}
}
